package com.spbl.common.exception;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.spbl.common.config.ErrorTypeEnum;
import com.spbl.common.config.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.StringJoiner;

/**
 * Description(异常处理)
 * author: Gao xueyong
 * Create at: 2020/4/12 5:40 下午
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final String url = "url";
    private static final String param = "param";
    private static final String errorMsg = "errorMsg";

    /**
     * 处理自定义异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    public Result handleBizException(HttpServletRequest req, BusinessException ex) {
        if (null == ex.getCode()) {
            return Result.renderError(ErrorTypeEnum.COMMON_ERROR.getCode(), ex.getMessage());
        }
        log.error(url + ":{}, " + param + ":{}, " + errorMsg + ":{}", req.getRequestURI(), JSONObject.toJSON(req.getParameterMap()), ex.getMessage(), ex);
        return Result.renderError(ex.getCode(), ex.getMessage());
    }

    /**
     * 参数绑定错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(BindException.class)
    public Result handleBindException(HttpServletRequest req, BindException ex) {
        StringJoiner sj = new StringJoiner(";");
        ex.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));
        log.error(url + ":{}, " + param + ":{}, " + errorMsg + ":{}", req.getRequestURI(), JSONObject.toJSON(req.getParameterMap()), sj.toString(), ex);
        return Result.renderError(ErrorTypeEnum.PARAM_ERROR.getCode(), sj.toString());
    }


    /**
     * 参数校验错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(ValidationException.class)
    public Result handleValidationException(HttpServletRequest req, ValidationException ex) {
        StringJoiner sj = new StringJoiner(";");
        if (ex instanceof ConstraintViolationException) {
            ConstraintViolationException cve = (ConstraintViolationException) ex;
            ((ConstraintViolationException) ex).getConstraintViolations().stream().forEach(x -> sj.add(x.getMessage()));
        }
        log.error(url + ":{}, " + param + ":{}, " + errorMsg + ":{}", req.getRequestURI(), JSONObject.toJSON(req.getParameterMap()), sj.toString(), ex);
        return Result.renderError(ErrorTypeEnum.PARAM_ERROR.getCode(), sj.toString());
    }


    /**
     * 字段校验不通过异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValidException(HttpServletRequest req, MethodArgumentNotValidException ex) {
        StringJoiner sj = new StringJoiner(";");
        ex.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));
        log.error(url + ":{}, " + param + ":{}, " + errorMsg + ":{}", req.getRequestURI(), JSONObject.toJSON(req.getParameterMap()), sj.toString(), ex);
        return Result.renderError(ErrorTypeEnum.PARAM_ERROR.getCode(), sj.toString());
    }

    /**
     * 字段类型转换错误(requestBody请求类型参数 转换json错误)
     * @param req
     * @param ex
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result handleHttpMessageNotReadableException(HttpServletRequest req, HttpMessageNotReadableException ex) {
        String msg ="";
        Throwable cause = ((HttpMessageNotReadableException) ex).getCause();
        InvalidFormatException invalidFt = (InvalidFormatException) cause;
        for(JsonMappingException.Reference jr:invalidFt.getPath()){
            msg =  "传入参数["+jr.getFieldName()+"]的值与所需参数格式的值不匹配！所需参数类型:" + invalidFt.getTargetType().getName()+"，传入参数值:"+invalidFt.getValue();
            break;
        }
        log.error(url + ":{}, " + param + ":{}, " + errorMsg + ":{}", req.getRequestURI(), JSONObject.toJSON(req.getParameterMap()), msg, ex);
        return Result.renderError(ErrorTypeEnum.PARAM_ERROR.getCode(), msg);
    }

    /**
     * 字段类型转换错误
     * @param req
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result handleMethodArgumentTypeMismatchException(HttpServletRequest req, MethodArgumentTypeMismatchException ex) {
        String msg = "[" + ex.getName() + " = " + ex.getValue() + "]" + ex.getMessage();
        log.error(url + ":{}, " + param + ":{}, " + errorMsg + ":{}", req.getRequestURI(), JSONObject.toJSON(req.getParameterMap()), msg, ex);
        return Result.renderError(ErrorTypeEnum.PARAM_ERROR.getCode(), msg);
    }

    /**
     * Controller参数绑定错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result handleMissingServletRequestParameterException(HttpServletRequest req, MissingServletRequestParameterException ex) {
        log.error(url + ":{}, " + param + ":{}, " + errorMsg + ":{}", req.getRequestURI(), JSONObject.toJSON(req.getParameterMap()), ex.getMessage(), ex);
        return Result.renderError(ErrorTypeEnum.PARAM_ERROR.getCode(), ex.getMessage());
    }

    /**
     * 其他未知异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public Result handleException(HttpServletRequest req, Exception ex) {
        log.error(url + ":{}, " + param + ":{}, " + errorMsg + ":{}", req.getRequestURI(), JSONObject.toJSON(req.getParameterMap()), ex.getMessage(), ex);
        return Result.renderError(ErrorTypeEnum.COMMON_ERROR.getCode(), ex.getMessage());
    }

}
